﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.Xml;
using System.Runtime.Serialization;
using System.IO;

namespace Shared
{
    /// <summary>
    /// A custom wcf message inspector to intercept wcf message and massage it for our needs.
    /// </summary>
    public class CustomMessageInspector : IDispatchMessageInspector, IClientMessageInspector
    {
        #region Message Inspector of the Service

        /// <summary>
        /// Called after an inbound message has been received but before the message is dispatched to the intended operation.
        /// </summary>
        /// <param name="request">The request message.</param>
        /// <param name="channel">The incoming channel.</param>
        /// <param name="instanceContext">The current service instance.</param>
        /// <returns>
        /// The object used to correlate state. This object is passed back in the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.BeforeSendReply(System.ServiceModel.Channels.Message@,System.Object)"/> method.
        /// </returns>
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            string clientID = request.Headers.GetHeader<string>(CustomHeaderConstants.ClientIDKey, CustomHeaderConstants.CustomHeaderNamespace);
            ClientContextHelper.SetCurrentClientID(clientID);
            if (clientID != "")
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    DataContractSerializer s = new DataContractSerializer(typeof(DefaultFaultContract));
                    s.WriteObject(ms, new DefaultFaultContract() { FaultMessage = "Fault Message" });
                    ms.Position = 0;
                    using (StreamReader sr = new StreamReader(ms))
                    {
                        var m = new FaultException(sr.ReadToEnd());
                        throw m;
                    }
                }
                //throw new FaultException<DefaultFaultContract>(new DefaultFaultContract(){FaultMessage="Fault Message"},new FaultReason("My Fault Reason"));
            }
            return null;
        }

        /// <summary>
        /// Called after the operation has returned but before the reply message is sent.
        /// </summary>
        /// <param name="reply">The reply message. This value is null if the operation is one way.</param>
        /// <param name="correlationState">The correlation object returned from the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.AfterReceiveRequest(System.ServiceModel.Channels.Message@,System.ServiceModel.IClientChannel,System.ServiceModel.InstanceContext)"/> method.</param>
        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            // Do nothing
        }

        #endregion

        #region Message Inspector of the Consumer

        /// <summary>
        /// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
        /// </summary>
        /// <param name="reply">The message to be transformed into types and handed back to the client application.</param>
        /// <param name="correlationState">Correlation state data.</param>
        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            // Do nothing
        }

        /// <summary>
        /// Enables inspection or modification of a message before a request message is sent to a service.
        /// </summary>
        /// <param name="request">The message to be sent to the service.</param>
        /// <param name="channel">The  client object channel.</param>
        /// <returns>
        /// The object that is returned as the <paramref name="correlationState "/>argument of the <see cref="M:System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply(System.ServiceModel.Channels.Message@,System.Object)"/> method. This is null if no correlation state is used.The best practice is to make this a <see cref="T:System.Guid"/> to ensure that no two <paramref name="correlationState"/> objects are the same.
        /// </returns>
        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            string clientID = "Default Client"; //TODO read from session
            var header = new MessageHeader<string>(clientID);
            var untyped = header.GetUntypedHeader(CustomHeaderConstants.ClientIDKey, CustomHeaderConstants.CustomHeaderNamespace);
            request.Headers.Add(untyped);
            return null;
        }

        #endregion
    }

    [Serializable]
    public class MyCustomFault : FaultException
    {
        public MyCustomFault(string reason)
            : base(reason)
        {

        }
    }
}
